package cn.cdeden.resource.service.impl;

import cn.cdeden.common.oss.core.OssClientBucket;
import cn.cdeden.common.oss.enumd.OssBucketOneEnum;
import cn.cdeden.common.oss.enumd.OssModuleType;
import cn.cdeden.common.satoken.utils.LoginHelper;
import cn.cdeden.resource.domain.vo.SysOssVersionVo;
import cn.cdeden.resource.service.ISysOssVersionService;
import cn.cdeden.resource.service.OssFileEsService;
import cn.cdeden.system.api.RemoteUserService;
import cn.cdeden.system.api.model.LoginUser;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import cn.cdeden.common.core.constant.CacheNames;
import cn.cdeden.common.core.exception.ServiceException;
import cn.cdeden.common.core.utils.MapstructUtils;
import cn.cdeden.common.core.utils.SpringUtils;
import cn.cdeden.common.core.utils.StringUtils;
import cn.cdeden.common.core.utils.file.FileUtils;
import cn.cdeden.common.mybatis.core.page.PageQuery;
import cn.cdeden.common.core.utils.page.TableDataInfo;
import cn.cdeden.common.oss.entity.UploadResult;
import cn.cdeden.common.oss.factory.OssFactory;
import cn.cdeden.resource.domain.SysOss;
import cn.cdeden.resource.domain.bo.SysOssBo;
import cn.cdeden.resource.domain.vo.SysOssVo;
import cn.cdeden.resource.mapper.SysOssMapper;
import cn.cdeden.resource.service.ISysOssService;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 文件上传 服务层实现
 *
 * @author Lion Li
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class SysOssServiceImpl extends ServiceImpl<SysOssMapper, SysOss> implements ISysOssService {

    private final SysOssMapper baseMapper;

    private final ISysOssVersionService ossVersionService;

    private OssFileEsService ossFileEsService; // es

    @DubboReference
    private final RemoteUserService remoteUserService;


    /**
     * 查询OSS对象存储列表
     *
     * @param bo        OSS对象存储分页查询对象
     * @param pageQuery 分页查询实体类
     * @return 结果
     */
    @Override
    public TableDataInfo<SysOssVo> queryPageList(SysOssBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<SysOss> lqw = buildQueryWrapper(bo);
        Page<SysOssVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        List<SysOssVo> filterResult = result.getRecords().stream().map(this::matchingUrl).collect(Collectors.toList());
        result.setRecords(filterResult);
        return TableDataInfo.build(result);
    }

    /**
     * 根据一组 ossIds 获取对应的 SysOssVo 列表
     *
     * @param ossIds 一组文件在数据库中的唯一标识集合
     * @return 包含 SysOssVo 对象的列表
     */
    @Override
    public List<SysOssVo> listByIdList(Collection<Long> ossIds) {
        List<SysOssVo> list = new ArrayList<>();
        SysOssServiceImpl ossService = SpringUtils.getAopProxy(this);
        for (Long id : ossIds) {
            SysOssVo vo = ossService.getById(id);
            if (ObjectUtil.isNotNull(vo)) {
                try {
                    list.add(this.matchingUrl(vo));
                } catch (Exception ignored) {
                    // 如果oss异常无法连接则将数据直接返回
                    list.add(vo);
                }
            }
        }
        return list;
    }

    /**
     * 根据一组 ossIds 获取对应文件的 URL 列表
     *
     * @param ossIds 以逗号分隔的 ossId 字符串
     * @return 以逗号分隔的文件 URL 字符串
     */
    @Override
    public String selectUrlByIds(String ossIds) {
        List<String> list = new ArrayList<>();
        SysOssServiceImpl ossService = SpringUtils.getAopProxy(this);
        for (Long id : StringUtils.splitTo(ossIds, Convert::toLong)) {
            SysOssVo vo = ossService.getById(id);
            if (ObjectUtil.isNotNull(vo)) {
                try {
                    list.add(this.matchingUrl(vo).getUrl());
                } catch (Exception ignored) {
                    // 如果oss异常无法连接则将数据直接返回
                    list.add(vo.getUrl());
                }
            }
        }
        return String.join(StringUtils.SEPARATOR, list);
    }

    @Override
    public String selectImageUrlByIds(String ossIds) {
        List<String> list = new ArrayList<>();
        SysOssServiceImpl ossService = SpringUtils.getAopProxy(this);
        for (Long id : StringUtils.splitTo(ossIds, Convert::toLong)) {
            SysOssVo vo = ossService.getById(id);
            if (ObjectUtil.isNotNull(vo)) {
                try {
                    list.add(this.matchingUrl(vo).getCoverUrl());
                } catch (Exception ignored) {
                    // 如果oss异常无法连接则将数据直接返回
                    list.add(vo.getUrl());
                }
            }
        }
        return String.join(StringUtils.SEPARATOR, list);
    }

    private LambdaQueryWrapper<SysOss> buildQueryWrapper(SysOssBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<SysOss> lqw = Wrappers.lambdaQuery();
        lqw.like(StringUtils.isNotBlank(bo.getFileName()), SysOss::getFileName, bo.getFileName());
        lqw.like(StringUtils.isNotBlank(bo.getOriginalName()), SysOss::getOriginalName, bo.getOriginalName());
        lqw.eq(StringUtils.isNotBlank(bo.getFileSuffix()), SysOss::getFileSuffix, bo.getFileSuffix());
        lqw.eq(StringUtils.isNotBlank(bo.getUrl()), SysOss::getUrl, bo.getUrl());
        lqw.eq(StringUtils.isNotBlank(bo.getModuleType()), SysOss::getModuleType, bo.getModuleType());
        lqw.between(params.get("beginCreateTime") != null && params.get("endCreateTime") != null,
            SysOss::getCreateTime, params.get("beginCreateTime"), params.get("endCreateTime"));
        lqw.eq(ObjectUtil.isNotNull(bo.getCreateBy()), SysOss::getCreateBy, bo.getCreateBy());
        lqw.eq(StringUtils.isNotBlank(bo.getService()), SysOss::getService, bo.getService());
        lqw.orderByAsc(SysOss::getOssId);
        return lqw;
    }

    /**
     * 根据 ossId 从缓存或数据库中获取 SysOssVo 对象
     *
     * @param ossId 文件在数据库中的唯一标识
     * @return SysOssVo 对象，包含文件信息
     */
//    @Cacheable(cacheNames = CacheNames.SYS_OSS, key = "#ossId")
    @Override
    public SysOssVo getById(Long ossId) {
        return baseMapper.selectVoById(ossId);
    }

    @Override
    public SysOssVo getByIdAndVersionId(Long ossId, String versionId) {
        SysOssVo sysOssVo = baseMapper.selectVoById(ossId);
        if (StringUtils.isNotBlank(versionId)) {
            SysOssVersionVo byIdAndVersionId = ossVersionService.getByIdAndVersionId(ossId, versionId);
            if (byIdAndVersionId != null) {
                sysOssVo.setVersionId(byIdAndVersionId.getVersionId());
                sysOssVo.setVersionName(byIdAndVersionId.getVersionName());
                sysOssVo.setFileSize(byIdAndVersionId.getFileSize());
                sysOssVo.setFileMd5(byIdAndVersionId.getFileMd5());
                return sysOssVo;
            } else {
                throw new ServiceException("版本号不存在");
            }
        } else {
            return sysOssVo;
        }
    }

    @Override
    public SysOssVo getByUrl(String url) {
        OssClientBucket storage = OssFactory.instanceBucket();
        String replace = url.replace(storage.getUrl() + StringUtils.SLASH, "");
        replace = StringUtils.substring(replace, 0, replace.lastIndexOf("?"));
        String bucketName = StringUtils.substring(replace, 0, replace.lastIndexOf(StringUtils.SLASH) + 1);
        String key = StringUtils.substring(replace, replace.lastIndexOf(StringUtils.SLASH) + 1, replace.length());
        SysOss one = baseMapper.lambdaQuery().eq(SysOss::getBucketName, bucketName).eq(SysOss::getFileName, key).one();

        return MapstructUtils.convert(one, SysOssVo.class);
    }


    /**
     * 文件下载方法，支持一次性下载完整文件
     *
     * @param ossId    OSS对象ID
     * @param response HttpServletResponse对象，用于设置响应头和向客户端发送文件内容
     */
    @Override
    public void download(Long ossId, HttpServletResponse response) throws IOException {
        SysOssVo sysOss = SpringUtils.getAopProxy(this).getById(ossId);
        if (ObjectUtil.isNull(sysOss)) {
            throw new ServiceException("文件数据不存在!");
        }
        FileUtils.setAttachmentResponseHeader(response, sysOss.getOriginalNameInfo());
        response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE + "; charset=UTF-8");
        OssClientBucket storage = OssFactory.instanceBucket(sysOss.getService());
        long contentLength = storage.download(sysOss.getBucketName(), sysOss.getFileName(), response.getOutputStream());
        response.setContentLengthLong(contentLength);
    }


    /**
     * 上传文件 生成结果实体
     *
     * @param fileName
     * @param suffix
     * @param configKey
     * @param uploadResult
     * @return cn.cdeden.resource.domain.vo.SysOssVo
     * @author Yu.
     * @Date 2024/12/5 15:00
     **/
    public SysOssVo buildResultEntity(String fileName, String suffix, String configKey, UploadResult uploadResult, OssModuleType moduleType) {
        SysOss oss = SysOss.builder()
            .url(uploadResult.getUrl())
            .fileSuffix(suffix)
            .fileName(uploadResult.getFilename())
            .originalName(fileName)
            .service(configKey)
            .bucketName(uploadResult.getBucketName())
            .versionId(uploadResult.getVersionId())
            .versionName(uploadResult.getVersionName())
            .moduleType(moduleType.getCode()) //先写死 //都是临时的
            .fileMd5(uploadResult.getFileMd5())
            .fileSize(uploadResult.getFileSize())
            .coverName(uploadResult.getCoverName())
            .coverUrl(uploadResult.getCoverUrl())
            .build();
        baseMapper.insert(oss);
        SysOssVo sysOssVo = MapstructUtils.convert(oss, SysOssVo.class);
        SysOssVo ossVo = addVersionDate(sysOssVo); //生成历史版本
        return this.matchingUrl(sysOssVo);
    }

    /**
     * 上传文件 生成结果实体 手动设置 用户信息
     *
     * @param fileName
     * @param suffix
     * @param configKey
     * @param uploadResult 上传文件的结果
     * @param moduleType   文件的模块
     * @param loginUser    用户信息
     * @return cn.cdeden.resource.domain.vo.SysOssVo
     * @author Yu.
     * @Date 2025/1/2 10:07
     **/
    public SysOssVo buildResultEntity(String fileName, String suffix, String configKey, UploadResult uploadResult, OssModuleType moduleType, LoginUser loginUser) {
        SysOss oss = new SysOss();
        oss.setUrl(uploadResult.getUrl());
        oss.setFileSuffix(suffix);
        oss.setFileName(uploadResult.getFilename());
        oss.setOriginalName(fileName);
        oss.setService(configKey);
        oss.setBucketName(uploadResult.getBucketName());
        oss.setVersionId(uploadResult.getVersionId());
        oss.setVersionName(uploadResult.getVersionName());
        oss.setModuleType(moduleType.getCode()); //先写死 //都是临时的
        oss.setFileMd5(uploadResult.getFileMd5());
        oss.setFileSize(uploadResult.getFileSize());
        oss.setCreateDept(loginUser.getDeptId());
        oss.setCreateBy(loginUser.getUserId());
        oss.setUpdateBy(loginUser.getUserId());
        baseMapper.insert(oss);
        SysOssVo sysOssVo = MapstructUtils.convert(oss, SysOssVo.class);
        SysOssVo ossVo = addVersionDate(sysOssVo); //生成历史版本
        return this.matchingUrl(sysOssVo);
    }


    /**
     * 修改OSS文件的 版本信息
     *
     * @param ossId
     * @param uploadResult //上传的文件信息
     * @param moduleType
     * @param userId       //用户信息 没有传用登录信息 传了用userId
     * @return boolean
     * @author Yu.
     * @Date 2024/12/5 20:14
     **/
    @Override
    public SysOssVo update(Long ossId, UploadResult uploadResult, OssModuleType moduleType, Long userId) {
        LoginUser loginUser = null;
        if (ObjectUtil.isNull(userId)) {
            loginUser = LoginHelper.getLoginUser();
        } else {
            loginUser = remoteUserService.getUserInfo(userId);
        }

        SysOssVo ossVo = this.getById(ossId);
        String moduleTypeCode = moduleType.getCode();
        Date date = new Date();
        baseMapper.lambdaUpdate()
            .eq(SysOss::getOssId, ossId)
            .set(uploadResult.getCoverUrl() != null , SysOss::getCoverUrl, uploadResult.getCoverUrl())
            .set(uploadResult.getCoverName() != null , SysOss::getCoverName, uploadResult.getCoverName())
            .set(SysOss::getVersionId, uploadResult.getVersionId())
            .set(SysOss::getVersionName, uploadResult.getVersionName())
            .set(SysOss::getFileSize, uploadResult.getFileSize())
            .set(SysOss::getFileMd5, uploadResult.getFileMd5())
            .set(SysOss::getModuleType, moduleTypeCode)
            .set(SysOss::getUpdateBy, loginUser.getUserId())
            .set(SysOss::getUpdateTime, date)
            .update(); //修改数据
        ossVo.setVersionId(uploadResult.getVersionId());
        ossVo.setVersionName(uploadResult.getVersionName());
        ossVo.setFileMd5(uploadResult.getFileMd5());
        ossVo.setFileSize(uploadResult.getFileSize());
        ossVo.setModuleType(moduleTypeCode);
        ossVo.setUpdateBy(loginUser.getUserId());
        ossVo.setUpdateTime(date);

        ossVo.setCreateTime(date); //创建时间
        ossVo.setCreateDept(loginUser.getDeptId());
        ossVo.setCreateBy(loginUser.getUserId());


        SysOssVo sysOssVo = addVersionDate(ossVo);//生成历史版本
        return ossVo;
    }


    /**
     * 添加 版本信息
     *
     * @param oss
     * @return cn.cdeden.resource.domain.vo.SysOssVo
     * @author Yu.
     * @Date 2024/12/6 14:34
     **/
    private SysOssVo addVersionDate(SysOssVo oss) {
        SysOssVersionVo sysOssVersionVo = ossVersionService.buildResultEntity(oss);
        return oss;
    }


    /**
     * 删除OSS对象存储
     *
     * @param ids     OSS对象ID串
     * @param isValid 判断是否需要校验
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if (isValid) {
            // 做一些业务上的校验,判断是否需要校验
        }
        log.info("删除OSS对象存储，主键为：{}", ids);

        List<SysOssVersionVo> byIdsList = ossVersionService.getByIdsList(ids);
        for (SysOssVersionVo sysOssVo : byIdsList) {
            OssClientBucket storage = OssFactory.instanceBucket(sysOssVo.getService());
            storage.delete(sysOssVo.getBucketName(), sysOssVo.getUrl());
        }


        List<SysOss> sysOssList = baseMapper.selectByIds(ids);

        List<String> coverNameList = sysOssList.stream().map(SysOss::getCoverUrl).filter(StringUtils::isNotEmpty).collect(Collectors.toList());
        OssClientBucket storage = OssFactory.instanceBucket();
        for (String coverUrl : coverNameList) {
            storage.delete(OssBucketOneEnum.PUBLIC_IMAGES.getCode(), coverUrl);
        }

        List<String> collect = sysOssList.stream().map(SysOss::getEsId).filter(StringUtils::isNotEmpty).collect(Collectors.toList());
        if (collect.size() > 0) {
            ossFileEsService.deleteByIds(collect);
        }
        ossVersionService.deleteWithValidByOssIds(ids, isValid); ////删除历史版本
        return baseMapper.deleteByIds(ids) > 0;
    }


    /**
     * 桶类型为 private 的URL 修改为临时URL时长为120s
     *
     * @param oss OSS对象
     * @return oss 匹配Url的OSS对象
     */
    private SysOssVo matchingUrl(SysOssVo oss) {
        OssClientBucket ossClientBucket = OssFactory.instanceBucket();
        String bucketName = oss.getBucketName();
        if (!ossClientBucket.bucketPolicy(bucketName)) {
            String privateUrl = ossClientBucket.getPrivateUrl(bucketName, oss.getFileName(), oss.getVersionId(), 120);
            oss.setUrl(privateUrl);
        }
        return this.matchingImageUrl(oss);
    }


    /**
     * 封面URL 如果没有url 着会设置默认图片
     *
     * @param oss
     * @return cn.cdeden.resource.domain.vo.SysOssVo
     * @author Yu.
     * @Date 2025/2/6 14:56
     **/
    private SysOssVo matchingImageUrl(SysOssVo oss) {
        oss.setCoverUrl(oss.getCoverUrl() != null ? oss.getCoverUrl() : "default"); // 如果 url 为空 设置默认图片
        return oss;
    }


}
